package com.easylinkin.linkappapi.mechanical.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.easylinkin.linkappapi.crane.entity.WorkTime;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class CraneUtil {

    /**
     * 圆心取大臂根部位置坐圆心，圆心坐标（x2,y2）如图所示：
     * 现场安装情况：主从天线安装在大臂的左侧或右侧，与大臂方向平行；
     * 主天线的坐标（x1,y1）已知（北斗定位的坐标）；
     * 图中所示的主从天线的距离y/从天线到圆心的距离x：现场安装时会提供（每个设备的这两个值可能会不一样，根据具体设备安装提供；x,y会根据如图所示象限给出正负值）；
     * 已圆心位置画象限：x2+x=x1;y2+y=y1;按照此公式可得出圆心坐标（x2,y2）；
     */
    // 根据吊车偏移量计算吊车位置
    // 地球半径，单位为米
    private static final double EARTH_RADIUS = 6371000;


    /**
     * 计算新的坐标点 根据角度和偏移量计算
     *
     * @param x1 原始点的x坐标
     * @param y1 原始点的y坐标
     * @param x  x轴偏移量（单位：米）
     * @param y  y轴偏移量（单位：米）
     * @param angle 角度（0-360度，0度为正北方向，顺时针为正）
     * @return 新的坐标点 [x2, y2]
     */
    public static double[] calculateNewCoordinatesNew1(double x1, double y1, double x, double y, double angle) {
        // 将角度转换为弧度
        double angleRad = Math.toRadians(angle);

        // 坐标系旋转变换
        // 新的偏移量 = 原偏移量按角度旋转
        double rotatedX = x * Math.cos(angleRad) + y * Math.sin(angleRad);
        double rotatedY = x * Math.sin(angleRad) - y * Math.cos(angleRad);

        // 计算新的坐标
        BigDecimal bd1 = new BigDecimal(rotatedX).divide(new BigDecimal(100000));
        BigDecimal bd2 = new BigDecimal(rotatedY).divide(new BigDecimal(100000));
        double dx = new BigDecimal(x1).add(bd1).doubleValue();
        double dy = new BigDecimal(y1).add(bd2).doubleValue();

        return new double[]{dx, dy};
    }

    /**
     * 计算新的坐标点 根据角度和偏移量计算
     *
     * @param x1 原始点的x坐标
     * @param y1 原始点的y坐标
     * @param x  x轴偏移量（单位：米）
     * @param y  y轴偏移量（单位：米）
     * @param angle 角度（0-360度，0度为正北方向，顺时针为正）
     * @return 新的坐标点 [x2, y2]
     */
    public static double[] calculateNewCoordinatesNew(double x1, double y1, double x, double y, double angle) {
        // 将角度转换为弧度
        double angleRad = Math.toRadians(angle);
        
        // 坐标系旋转变换
        // 新的偏移量 = 原偏移量按角度旋转
        double rotatedX = x * Math.cos(angleRad) - y * Math.sin(angleRad);
        double rotatedY = x * Math.sin(angleRad) + y * Math.cos(angleRad);
        
        // 计算新的坐标
        BigDecimal bd1 = new BigDecimal(rotatedX).divide(new BigDecimal(100000));
        BigDecimal bd2 = new BigDecimal(rotatedY).divide(new BigDecimal(100000));
        double dx = new BigDecimal(x1).add(bd1).doubleValue();
        double dy = new BigDecimal(y1).add(bd2).doubleValue();
        
        return new double[]{dx, dy};
    }
    
    /**
     * 计算新的坐标点 直接用x,y轴偏移量计算（保留原方法兼容性）
     *
     * @param x1 原始点的x坐标
     * @param y1 原始点的y坐标
     * @param x  x轴偏移量（单位：米）
     * @param y  y轴偏移量（单位：米）
     * @return 新的坐标点 [x2, y2]
     */
    public static double[] calculateNewCoordinatesNew(double x1, double y1, double x, double y) {
        // 调用新方法，角度为0度（正北方向）
        return calculateNewCoordinatesNew(x1, y1, x, y, 0.0);
    }

    /**
     * 计算新的经纬度点
     *
     * @param x1 原始点的经度
     * @param y1 原始点的纬度
     * @param x  x轴偏移量（单位：米）
     * @param y  y轴偏移量（单位：米）
     * @return 新的经纬度点（x2, y2）
     */
    public static double[] calculateNewCoordinates(double x1, double y1, double x, double y) {
        // 将经纬度转换为弧度
        double lon1Rad = Math.toRadians(x1);
        double lat1Rad = Math.toRadians(y1);

        // 计算新的经度
        double newLonRad = lon1Rad + x / (EARTH_RADIUS * Math.cos(lat1Rad));
        double newLon = Math.toDegrees(newLonRad);

        // 计算新的纬度
        double newLatRad = lat1Rad + y / EARTH_RADIUS;
        double newLat = Math.toDegrees(newLatRad);


        return new double[]{newLon, newLat};
    }

    /**
     * 反余弦计算方式
     *
     * @param longitude1 第一个点的经度
     * @param latitude1  第一个点的纬度
     * @param longitude2 第二个点的经度
     * @param latitude2  第二个点的纬度
     * @return 返回距离，单位m
     */
    public static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
        // 纬度
        double lat1 = Math.toRadians(latitude1);
        double lat2 = Math.toRadians(latitude2);
        // 经度
        double lon1 = Math.toRadians(longitude1);
        double lon2 = Math.toRadians(longitude2);
        // 纬度之差
        double a = lat1 - lat2;
        // 经度之差
        double b = lon1 - lon2;
        // 计算两点距离的公式
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
        // 弧长乘赤道半径, 返回单位: 米
        s = s * EARTH_RADIUS;
        return s;
    }


    /**
     * 计算点到多边形的最短距离边的中点
     *
     * @param lng 经度
     * @param lat 纬度
     * @param location 多边形的顶点坐标列表
     * @return
     */
    public static double[] calculateClosestPointOnLine(String lng, String lat, String location) {
        // 计算垂直距离
        // 示例：点的坐标
        Point2D.Double point = new Point2D.Double(Double.valueOf(lng), Double.valueOf(lat));
        // 示例：多边形的顶点坐标列表
        // [[122.32318331635162,41.40930740924327,-0.0005703735568374469],[122.32506925553089,41.41003196303127,-0.0005828391569639631],[122.32520367736875,41.40971647417358,-0.0005684721573634028],[122.32341267893523,41.408974201076695,-0.0005773849738319483]]
        List<Point2D.Double> polygon = new ArrayList<>();
        JSONArray locationArr = JSON.parseArray(location);
        for (int i = 0; i < locationArr.size(); i++) {
            JSONArray pointArr = locationArr.getJSONArray(i);
            if (pointArr.size() >= 2) {
                double lng1 = pointArr.getDouble(0);
                double lat1 = pointArr.getDouble(1);
                polygon.add(new Point2D.Double(lng1, lat1));
            }
        }
        // 转换为平面坐标系
        List<Point2D.Double> projectedPolygon = new ArrayList<>();
        for (Point2D.Double p : polygon) {
            double[] projected = convertToProjectedCoordinate(p.getX(), p.getY(), lat);
            projectedPolygon.add(new Point2D.Double(projected[0], projected[1]));
        }
        
        double[] projectedPoint = convertToProjectedCoordinate(point.getX(), point.getY(), lat);
        Point2D.Double projectedTargetPoint = new Point2D.Double(projectedPoint[0], projectedPoint[1]);
        
        // 在平面坐标系中计算垂直点
        PointToPolygonDistanceWithFoot.DistanceResult result = 
            PointToPolygonDistanceWithFoot.calculateMinDistanceWithFootPlanar(projectedTargetPoint, projectedPolygon);
        
        // 将结果转换回经纬度
        double[] footPointLatLng = convertBackToLatLng(result.footPoint.getX(), result.footPoint.getY(), lat);
        
        return new double[]{footPointLatLng[0], footPointLatLng[1], result.distance};
    }

    // 简化的平面坐标转换（适用于小范围区域）
    private static double[] convertToProjectedCoordinate(double lng, double lat, String referenceLat) {
        double refLat = Double.parseDouble(referenceLat);
        double refLatRad = Math.toRadians(refLat);

        // 转换为米
        double x = lng * 111320.0 * Math.cos(refLatRad); // 经度转米
        double y = lat * 111320.0; // 纬度转米

        return new double[]{x, y};
    }

    private static double[] convertBackToLatLng(double x, double y, String referenceLat) {
        double refLat = Double.parseDouble(referenceLat);
        double refLatRad = Math.toRadians(refLat);

        double lng = x / (111320.0 * Math.cos(refLatRad));
        double lat = y / 111320.0;

        return new double[]{lng, lat};
    }


    /**
     * 一个点是否在多边形内
     *
     * @param point
     *            要判断的点的横纵坐标
     * @param polygon
     *            组成的顶点坐标集合
     * @return
     */
    public static boolean isInPolygon(String lat, String lng, String location) {
        // 示例：点的坐标
        Point2D.Double point = new Point2D.Double(Double.valueOf(lat), Double.valueOf(lng));
        // 示例：多边形的顶点坐标列表
        // [[122.32318331635162,41.40930740924327,-0.0005703735568374469],[122.32506925553089,41.41003196303127,-0.0005828391569639631],[122.32520367736875,41.40971647417358,-0.0005684721573634028],[122.32341267893523,41.408974201076695,-0.0005773849738319483]]
        List<Point2D.Double> polygon = new ArrayList<>();
        JSONArray locationArr = JSON.parseArray(location);
        for (int i = 0; i < locationArr.size(); i++) {
            JSONArray pointArr = locationArr.getJSONArray(i);
            if (pointArr.size() >= 2) {
                double lat1 = pointArr.getDouble(0);
                double lng1 = pointArr.getDouble(1);
                // double alt = Double.valueOf(locationArr[2]);
                polygon.add(new Point2D.Double(lat1, lng1));
            }
        }
        return isInPolygon(point, polygon);
    }

    /**
     * 一个点是否在多边形内
     *
     * @param point
     *            要判断的点的横纵坐标
     * @param polygon
     *            组成的顶点坐标集合
     * @return
     */
    public static boolean isInPolygon(Point2D.Double point, List<Point2D.Double> polygon) {
        java.awt.geom.GeneralPath peneralPath = new java.awt.geom.GeneralPath();

        Point2D.Double first = polygon.get(0);
        // 通过移动到指定坐标（以双精度指定），将一个点添加到路径中
        peneralPath.moveTo(first.x, first.y);
        polygon.remove(0);
        for (Point2D.Double d : polygon) {
            // 通过绘制一条从当前坐标到新指定坐标（以双精度指定）的直线，将一个点添加到路径中。
            peneralPath.lineTo(d.x, d.y);
        }
        // 将几何多边形封闭
        peneralPath.lineTo(first.x, first.y);
        peneralPath.closePath();
        // 测试指定的 Point2D 是否在 Shape 的边界内。
        return peneralPath.contains(point);
    }

    // 判断 HHmmss 是否包含在 HHmmss 范围内
    public static boolean isHHmmssInHHmmssRange(String hhmmss, WorkTime workTime) {
        String startTime = workTime.getStartTime();
        String endTime = workTime.getEndTime();
        if (hhmmss.compareTo(startTime) >= 0 && hhmmss.compareTo(endTime) <= 0) {
            return true;
        }
        return false;
    }

    public static boolean isHHmmssInHHmmssRangeList(String hhmmss, List<WorkTime> workTimeList) {
        for (WorkTime workTime : workTimeList) {
            if (isHHmmssInHHmmssRange(hhmmss, workTime)) {
                return true;
            }
        }
        return false;
    }


    public static void main(String[] args) {
//        // 示例：原始点的经纬度
//        double x1 = 29.80521228;  // 纬度 latitude
//        double y1 = 114.143669771; // 经度 longitude
//
//        // 偏移量（单位：米）
//        double x = 1.83; // x轴偏移量
//        double y = -0.3;  // y轴偏移量
//
//        // 测试不同角度的坐标计算
//        double[] angles = {0.0, 90.0, 180.0, 270.0, 45.0};
//
//        for (double angle : angles) {
//            double[] newCoordinates = calculateNewCoordinatesNew(x1, y1, x, y, angle);
//            System.out.println("角度 " + angle + "° 的新坐标: 经度=" + newCoordinates[1] + ", 纬度=" + newCoordinates[0]);
//        }

        // 示例：原始点的经纬度
        double x1 = 114.143669771; // 经度 longitude
        double y1 = 29.80521228;  // 纬度 latitude

        // 偏移量（单位：米）
        double x = -0.3; // x轴偏移量
        double y = 1.83;  // y轴偏移量

        // 测试不同角度的坐标计算
        double[] angles = {0.0, 90.0, 180.0, 270.0, 45.0};

        for (double angle : angles) {
            double[] newCoordinates = calculateNewCoordinatesNew1(x1, y1, x, y, angle);
            System.out.println("角度 " + angle + "° 的新坐标: 经度=" + newCoordinates[0] + ", 纬度=" + newCoordinates[1]);
            // 计算经度 纬度 与原来的差值 Bigdecimal
            BigDecimal diffLon = new BigDecimal(newCoordinates[0]).subtract(new BigDecimal(x1)).setScale(8, BigDecimal.ROUND_HALF_UP);
            BigDecimal diffLat = new BigDecimal(newCoordinates[1]).subtract(new BigDecimal(y1)).setScale(8, BigDecimal.ROUND_HALF_UP);;
            System.out.println("原来的差值: 经度=" + diffLon.toString() + ", 纬度=" + diffLat.toString());
        }

//        // 兼容性测试：不带角度参数的原方法
//        double[] newCoordinatesOriginal = calculateNewCoordinatesNew(x1, y1, x, y);
//        System.out.println("原方法（角度0°）的新坐标: 纬度=" + newCoordinatesOriginal[0] + ", 经度=" + newCoordinatesOriginal[1]);

//
//        // 示例：点的坐标
//        Point2D.Double point = new Point2D.Double(114.15997806796778, 30.613522643949864);
//
//        // 示例：多边形的顶点坐标列表
//        // [[122.32318331635162,41.40930740924327,-0.0005703735568374469],[122.32506925553089,41.41003196303127,-0.0005828391569639631],[122.32520367736875,41.40971647417358,-0.0005684721573634028],[122.32341267893523,41.408974201076695,-0.0005773849738319483]]
////        List<Point2D.Double> polygon = new ArrayList<>();
////        polygon.add(new Point2D.Double(116.390000, 39.900000));
////        polygon.add(new Point2D.Double(116.400000, 39.900000));
////        polygon.add(new Point2D.Double(116.400000, 39.910000));
////        polygon.add(new Point2D.Double(116.390000, 39.910000));
//        // [[114.15959949531714,30.614003545741696],[114.1598122951374,30.613712322662412],[114.16135576727723,30.614595309545454],[114.16105407679622,30.614858576950578]]
//        List<Point2D.Double> polygon = new ArrayList<>();
//        polygon.add(new Point2D.Double(114.15959949531714, 30.614003545741696));
//        polygon.add(new Point2D.Double(114.1598122951374, 30.613712322662412));
//        polygon.add(new Point2D.Double(114.16135576727723, 30.614595309545454));
//        polygon.add(new Point2D.Double(114.16105407679622, 30.614858576950578));
//
//        // 计算点到多边形的最短距离
//        PointToPolygonDistanceWithFoot.DistanceResult distanceResult = PointToPolygonDistanceWithFoot.calculateMinDistanceWithFoot(point, polygon);
//        double distance = distanceResult.distance;
//
//        // 输出结果
//        System.out.println("点到多边形的最短距离: " + distance);
//
//        System.out.println("最短距离边的点: 经度=" + distanceResult.footPoint.getX() + ", 纬度=" + distanceResult.footPoint.getY());
//
//        boolean isInOperationArea = CraneUtil.isInPolygon(point, polygon);
//        System.out.println("点是否在多边形内: " + isInOperationArea);
//
//
//
//        String hhmmss = "12:04:00";
//        List<WorkTime> workTimeList = new ArrayList<>();
//        workTimeList.add(new WorkTime("09:00:00", "11:00:00"));
//        workTimeList.add(new WorkTime("13:00:00", "15:00:00"));
//        workTimeList.add(new WorkTime("09:00:00", "17:00:00"));
//        for (WorkTime workTime : workTimeList) {
//            if (isHHmmssInHHmmssRange(hhmmss, workTime)) {
//                System.out.println(hhmmss + " 在工作时间范围内");
//            } else {
//                System.out.println(hhmmss + " 不在工作时间范围内");
//            }
//        }
//
//
//        String lng = "114.15997654199612";
//        String lat = "30.613552555440148";
//        String location = "[[114.15986142914068,30.613540834430918],[114.16001316096745,30.613363550987977],[114.16015557626102,30.6134648556275],[114.16002780216772,30.61363292967757]]";
//        boolean isInOperationArea1 = isInPolygon(lng, lat, location);
//        System.out.println("点是否在多边形内: " + isInOperationArea1);
    }
}
